/*
 * ClassHierarchyListFactory.java
 *
 * Created on December 5, 2002, 4:12 PM
 *
 * Copyright 2008 the Desert Labs Project (http://desertlabs.us)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package us.desertlabs;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * This factory provides methods for the construction of <CODE>List</CODE>s
 * of inheritance hierarchies of any given <CODE>Class</CODE> object. Unlike
 * the methods on <CODE>Class</CODE> itself, these <CODE>List</CODE>s
 * traverse the entire hierarchy, not just the immediate level.
 *
 * @author Brandon Franklin
 */
public class ClassHierarchyListFactory {

    /**
     * Given a <CODE>Class</CODE> object, returns a <CODE>List</CODE>
     * over all of the interfaces and superclasses represented by the <CODE>Class</CODE>.
     * In other words, all of the possible class types that the supplied <CODE>Class</CODE>
     * can be cast to will be in the <CODE>List</CODE>. The <CODE>List</CODE>
     * that is returned will contain the superclasses and interfaces in
     * breadth-first order, with each superclass followed by all of its
     * interfaces. The <CODE>Class</CODE> itself will be the first item in the
     * <CODE>List</CODE>, followed immediately by its interfaces.
     *
     * @param clazz the <CODE>Class</CODE> object to be analyzed
     * @return a <CODE>List</CODE> over all of the possible class types
     *         that the supplied <CODE>Class</CODE> can be cast to, starting
     *         with the supplied <CODE>Class</CODE> object
     */
    public static List<Class<?>> createAssignableList( final Class<?> clazz ) {

        final Set<Class<?>> set = new LinkedHashSet<Class<?>>();

        for( final Class<?> superClazz : createSuperclassList( clazz ) ) {
            set.add( superClazz );
            set.addAll( createInterfaceList( superClazz ) );
        }

        return Collections.unmodifiableList( new ArrayList<Class<?>>( set ) );
    }

    /**
     * Given a <CODE>Class</CODE> object, returns a <CODE>List</CODE>
     * of all of the interfaces represented by the <CODE>Class</CODE>. The
     * <CODE>List</CODE> that is returned will include the interfaces
     * in breadth-first order.
     *
     * @param clazz the <CODE>Class</CODE> object to be analyzed
     * @return a <CODE>List</CODE> of all of the interfaces represented
     *         by the supplied <CODE>Class</CODE>
     */
    public static List<Class<?>> createInterfaceList( final Class<?> clazz ) {

        final Set<Class<?>> set = new LinkedHashSet<Class<?>>();
        final List<Class<?>> interfaces = new LinkedList<Class<?>>();
        for( final Class<?> interfaze : clazz.getInterfaces() ) {
            interfaces.add( interfaze );
        }

        while( !interfaces.isEmpty() ) {
            final Class<?> interfaze = interfaces.remove( 0 );
            set.add( interfaze );
            for( final Class<?> superInterfaze : interfaze.getInterfaces() ) {
                interfaces.add( superInterfaze );
            }
        }

        return Collections.unmodifiableList( new ArrayList<Class<?>>( set ) );
    }

    /**
     * Given a <CODE>Class</CODE> object, returns a <CODE>List</CODE>
     * of all of the superclasses represented by the <CODE>Class</CODE>.
     * The supplied <CODE>Class</CODE> will, itself, be the first item in the
     * <CODE>List</CODE>, followed immediately by its first superclass.
     *
     * @param <T> the datatype for which the <CODE>Class</CODE> object is the class
     * @param clazz the <CODE>Class</CODE> object to be analyzed
     * @return a <CODE>List</CODE> of all of the superclasses
     *         represented by the supplied <CODE>Class</CODE>, starting with
     *         the supplied <CODE>Class</CODE> object
     */
    public static <T> List<Class<? super T>> createSuperclassList( final Class<T> clazz ) {

        final List<Class<? super T>> list = new LinkedList<Class<? super T>>();

        Class<? super T> superClazz = clazz;
        while( superClazz != null ) {
            list.add( superClazz );
            superClazz = superClazz.getSuperclass();
        }

        return Collections.unmodifiableList( list );
    }

    /**
     * Made private to prevent use.
     */
    private ClassHierarchyListFactory() {
    }
}
